/*
*    Phantom v2.0 GUI Automation Software
*    Copyright (C) 2009  John C. Van Arsdall (john@phantomtest.com)
*
*    This program is free software; you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation; either version 2 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License along
*    with this program; if not, write to the Free Software Foundation, Inc.,
*    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
// PtrArray.h: interface for the SSI_CPtrArray class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_PTRARRAY_H__3F1FB505_C6B5_4BC4_A4CF_B267F79112F6__INCLUDED_)
#define AFX_PTRARRAY_H__3F1FB505_C6B5_4BC4_A4CF_B267F79112F6__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <stdio.h>


//#include "ssi_defines.h"

#define IS_ZERO_BASED_ARRAYS 1

#define NULL_NODE   (DimNode)(0)
#define ERROR_NODE  (DimNode)(-1)

#define SSI_PTR     int


struct DimensionT;
typedef DimensionT* DimNode;

struct DimensionT
{
  DimNode Next;
  int Dim;
};

static DimNode new_array_node()
{
  DimNode Ret = new DimensionT;
  Ret->Next = 0;
  Ret->Dim = 0;
  return Ret;
}

static void del_array_node(DimNode In)
{
	if(!In) return;
	del_array_node(In->Next);
	In->Next = 0;
  delete In;
	In = 0;
}

static void set_array_node(DimNode In, int Dim)
{
	In->Dim = Dim + IS_ZERO_BASED_ARRAYS;
}

static DimNode copy_array_node(DimNode In)
{
	if(!In) return 0;
	DimNode Ret = new_array_node();
	if(In->Next){
		Ret->Next = copy_array_node(In->Next);
	}
	Ret->Dim = In->Dim;
	return Ret;
}


/// OK JERKY... THESE SSI_PTR ARRAYS ARE 1 BASED

template<class aType>
//class SSI_SCRIPTDLL_API SSI_CPtrArray  
class /*_declspec(dllexport)*/ SSI_CPtrArray  
{
public:
	
	DimNode get_next_elem(DimNode &StartLoc, char bIncludeNULLs = 0);
	int get_num_elements();
	int swap(int Ind1, int Ind2);
	aType get_at(DimNode Loc);
	aType get_at(int Ind);
	aType remove_ptr(DimNode Loc);
	aType remove_ptr(int Loc);
	SSI_PTR* set_at(DimNode Loc, aType NewData, int delete_old = 1);
	SSI_PTR* set_at(int Loc, aType NewData, int delete_old = 1);
  static DimNode new_node();
  void clear_dims(DimNode aNode = 0);
  void clear();
  DimNode get_dims();
  const SSI_PTR** get_array();

  void copy_dimensions(SSI_CPtrArray &In);
	DimNode copy_dims(DimNode In);  // Creates a DimNode and does not delete
	
	void set_array_ptr(SSI_PTR** new_ptr);

	SSI_CPtrArray();
	~SSI_CPtrArray();
protected:
  int update_array_size(DimNode NewDims);
 	void set_dimension(int Dim, int Size);
  SSI_PTR** update_memory(DimNode Dim, int new_size);
 	int product_subs(DimNode Dim);
 	int check_dim(DimNode Loc);
 	int to_index(DimNode Loc);

  int get_product(DimNode Loc, DimNode Tot);

	char justify(DimNode Check, DimNode& In);

  void delete_array();

private:
  DimNode Top;
  SSI_PTR** arr;
};

////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
template<class aType>
int SSI_CPtrArray<aType>::swap(int Ind1, int Ind2){
	if(!arr) return 0;
	if(Ind1 > get_num_elements()){
		return 0;
	}
	if(Ind2 > get_num_elements()){
		return 0;
	}
	if(Ind1 < 0) return 0;
	if(Ind2 < 0) return 0;


	if(!arr[Ind2]) return 0;
	if(!arr[Ind1]) return 0;
	
	aType temp = (aType)arr[Ind1];
	arr[Ind1] = arr[Ind2];
	arr[Ind2] = (int *)temp;
	return 1;
}

//Return val is SSI_NULL if at EOL, else is StartLoc... Regardless, StartLoc has the returned data
template<class aType>
DimNode SSI_CPtrArray<aType>::get_next_elem(DimNode &StartLoc, char bIncludeNULLs)
{
	DimNode CurrPar = 0;
	DimNode Curr = 0;
	DimNode MasterPar = 0;
	DimNode Master = 0;
	
	DimNode LocTop = Top;

	if(StartLoc == 0){
		StartLoc = copy_dims(Top);  //how do we track this and ensure deletion?
		Curr = StartLoc;
		while(Curr){   // Start at zero loc
			Curr->Dim = 1; // INTERNALLY, ALWAYS 1 BASED
			Curr = Curr->Next;
		}
	}
	Curr = StartLoc;

	if(!Curr){
		return 0;
	}

	// Returned DimNode is new pointer?  Or old?

	while(Curr->Next){
		Curr = Curr->Next;
	}
	Curr->Dim++;
	
	if(justify(Top, StartLoc)){
		// The new DimNode is greater than the total
		return 0;  // reached the end of the line
	}
	return StartLoc;  //return the new node location
}

template<class aType>
char SSI_CPtrArray<aType>::justify(DimNode Check, DimNode& In){
	DimNode Loc = Check;
	if(!In) return 0;
	if(!Check) return 0;

	if(justify(Loc->Next, In->Next)){
		In->Dim++;
	}

	if(In->Dim > (Loc->Dim)){
		In->Dim = 1;  // INTERNALLY, ALWAYS 1 BASED
		return 1;
	}
	return 0;
}

template<class aType>
void SSI_CPtrArray<aType>::copy_dimensions(SSI_CPtrArray<aType> &In)
{
  if(!In.Top) return;
  clear_dims();
  Top = copy_dims(In.Top);
}

template<class aType>
DimNode SSI_CPtrArray<aType>::copy_dims(DimNode In)
{
	if(!In) return 0;
	DimNode aNode = In;
  DimNode Curr = new_array_node();
	DimNode Ret = Curr;
  while(aNode)
  {
     Curr->Dim = aNode->Dim;
     aNode = aNode->Next;
     if(aNode)
     {
      Curr->Next = new_array_node();
      Curr = Curr->Next;
     }
     else
     {
       Curr->Next = 0;
     }
  }
	return Ret;
}

////////////////////////////////////////////////////////////////

template<class aType>
aType SSI_CPtrArray<aType>::remove_ptr(DimNode Loc)
{
  if(!Loc) return 0;
  if(!check_dim(Loc)) return 0;
  
  int index = to_index(Loc);
  if(!arr[index])
  {
    //cout << "ERROR";
    return 0;
  }
  aType itemp = (aType)arr[index];
  arr[index] = 0;
  return itemp;
}

template<class aType>
aType SSI_CPtrArray<aType>::remove_ptr(int Loc)
{  
  int index = Loc;
  if(index > get_num_elements()){
	  return 0;
  }
  if(!arr[index])
  {
    //cout << "ERROR";
    return 0;
  }
  aType itemp = (aType)arr[index];
  arr[index] = 0;
  return itemp;
}

////////////////////////////////////////////////////////////////


template<class aType>
void SSI_CPtrArray<aType>::clear()
{
  delete_array();
  clear_dims();
}

template<class aType>
SSI_PTR* SSI_CPtrArray<aType>::set_at(int Loc, aType NewData, int delete_old)
{
	// Construct the node from the index
	//DimNode aNode = new_array_node();
	//aNode->Dim = Loc+IS_ZERO_BASED_ARRAYS;    // Is 1 based... or something
	DimensionT aNode;
	aNode.Dim = Loc+IS_ZERO_BASED_ARRAYS;
	aNode.Next = 0;

	// Set the node at the index
	return set_at(&aNode, NewData, delete_old);
}

template<class aType>
SSI_PTR* SSI_CPtrArray<aType>::set_at(DimNode Loc, aType NewData, int delete_old)
{
  // Check for dimension location, and if not exist, add dimensions memory until it does
  update_array_size(Loc);
  
  int index = to_index(Loc);
  //index--; // because 1 based
  
	if(index < 0) return 0;

  aType itemp = NewData;
  
  if(arr[index] && delete_old)
  {
    int* i_ptr = (int*)arr[index];
    delete i_ptr;
  }

  arr[index] = reinterpret_cast<SSI_PTR*>(itemp);
  return (SSI_PTR*)itemp;
}

template<class aType>
const SSI_PTR** SSI_CPtrArray<aType>::get_array()
{
	return (const SSI_PTR**)arr;
}

template<class aType>
aType SSI_CPtrArray<aType>::get_at(DimNode Loc)
{
  // Check for dimension location, and if not exist, add dimensions memory until it does
  
  if(!check_dim(Loc)) return 0;
  
  int index = to_index(Loc);
	if(index < 0) return 0;
  if(!arr[index])
  {
    return 0;
  }

  aType itemp = (aType)arr[index];
  return itemp;
}

template<class aType>
aType SSI_CPtrArray<aType>::get_at(int Ind)
{
	if(Ind < 0) return 0;
	if(!arr) return 0;
	if(Ind > get_num_elements()){
		return 0;
	}
  if(!arr[Ind])
  {
    return 0;
  }
  aType itemp = (aType)arr[Ind];
  return itemp;
}

template<class aType>
void SSI_CPtrArray<aType>::clear_dims(DimNode aNode)
{
  if(!aNode && Top)
  {
    aNode = Top;
  }
  else
  {
		if(!aNode){
			return;
		}
  }
  if(aNode->Next)
  {
    clear_dims(aNode->Next);
    aNode->Next = 0;
  }
  delete aNode;
}

template<class aType>
void SSI_CPtrArray<aType>::set_array_ptr(SSI_PTR** new_ptr)
{
  delete_array();
  arr = new_ptr;
}

template<class aType>
DimNode SSI_CPtrArray<aType>::new_node()
{
	// 1 SSI_DISPLAY_FILE_NEW
  DimNode Ret = new DimensionT;
  Ret->Next = 0;
  Ret->Dim = 0;
  return Ret;
}

template<class aType>
DimNode SSI_CPtrArray<aType>::get_dims()
{
  return Top;
}

template<class aType>
int SSI_CPtrArray<aType>::get_num_elements()
{
  DimNode aNode = Top;
  int Ret = 1;
  if(Top == 0) return 0;
  while(aNode)
  {
    Ret = Ret * aNode->Dim;
    aNode = aNode->Next;
  }
  return Ret;
}

////////////////////////////////////////////////////////
////////////////////////////////////////////////////////

template<class aType>
void SSI_CPtrArray<aType>::delete_array()
{
  int i = get_num_elements();
  SSI_PTR** temp = arr;
	SSI_PTR* itemp = 0;
  while(i)
  {
    i--;
	  itemp = (SSI_PTR*)temp[i];
    if(itemp) delete itemp;
  }
  delete [] arr;
  arr = 0;
  clear_dims(Top);
  Top = 0;
}

template<class aType>
SSI_CPtrArray<aType>::SSI_CPtrArray()
{
  arr = 0;
  Top = 0;
}

template<class aType>
SSI_CPtrArray<aType>::~SSI_CPtrArray()
{
  delete_array();
}

template<class aType>
void SSI_CPtrArray<aType>::set_dimension(int DimNum, int Size)
{
  DimNode aDim = Top;
  DimNode upDim = 0;

  // Hmmm.... the dimension can be 1st dimension, yet another value
  if(!Top && (DimNum == 1)) // Nothing to do with number base... just is it the first dimension to set?
  {
		// 1 SSI_DISPLAY_FILE_NEW
    Top = new DimensionT;
    Top->Next = 0;
    Top->Dim = 0;
    upDim = Top;
    // use this dimension for update
  }
  else if(DimNum == 1)  // Is this the first dimension to update?
  {
    upDim = Top;
  }
  else
  {
    while(DimNum > 2)      // 2 Because will want *next* dimension after this loop
    {
      if(aDim->Next == 0)
      {
        //cout << "ERROR";
        return;
      }
      aDim = aDim->Next;
      DimNum--;
    }
    if(aDim->Next)
    {
      aDim = aDim->Next;
    }
    else
    {
      aDim->Next = new DimensionT;
      aDim = aDim->Next;
      aDim->Dim = 0;
      aDim->Next = 0;
    }
    upDim = aDim;
    // use this dimension for update
  }
  SSI_PTR** test = update_memory(upDim, Size);
  upDim->Dim = Size;
}

template<class aType>
SSI_PTR** SSI_CPtrArray<aType>::update_memory(DimNode Dim, int new_size)
{
  if(!Dim) return 0;    
  
  int old_size = Dim->Dim;
  if((old_size == 0) && arr) old_size = 1;
	if(new_size <= old_size) return 0;

	DimNode Curr = Dim;
	int prod_of_underling = 1;
	int prod_supers = 1;
	int size_new = 1;
	
	int i = 0;
  int j = 0;

	SSI_PTR** ptr_old = 0;
	SSI_PTR** ptr_new = 0;
  
  prod_of_underling = product_subs(Curr);
	
	Curr = Top;
	while(Curr != Dim)
	{
		prod_supers *= Curr->Dim;
		size_new *= Curr->Dim;
		
		Curr = Curr->Next;
    if(!Curr){
      return 0;
    }
	}

	size_new *= new_size * prod_of_underling;

	// 1 SSI_DISPLAY_FILE_NEW
	SSI_PTR** arr_new = new SSI_PTR*[size_new];
  SSI_PTR** temp_arr = arr_new;

	ptr_new = arr_new;
	ptr_old = arr;

  if(!arr)
  {
    arr = arr_new;
    for(i = 0; i < new_size; i++)
    {
      *(arr_new++) = 0;  // because a ptr...
    }
  }
  else
  {
	  for(i = 0; i < prod_of_underling; i++)
	  {
      for(j = 0; j < old_size*prod_supers; j++)
      {
        *(arr_new++) = *(ptr_old++);
      }

		  for(j = 0; j < (new_size - old_size)*prod_supers; j++)
		  {
			  *(arr_new++) = 0;  // because a ptr...
		  }
	  }
    delete arr;
    arr = temp_arr;
  }
  return arr;
}

template<class aType>
int SSI_CPtrArray<aType>::product_subs(DimNode Dim)
{
  if(!Dim) return 0;
  if(!Dim->Next) return 1;
  int Ret = 1;
  while(Dim->Next)
  {
    Dim = Dim->Next;
    Ret *= Dim->Dim;
  }
  return Ret;
}

template<class aType>
int SSI_CPtrArray<aType>::get_product(DimNode Loc, DimNode Tot)
{
  if(!Loc) return -1;
  if(!Tot) return -1;

  int Temp = 0;
  DimNode tLoc = Loc;
  DimNode tTot = Tot;
  if(tLoc->Next)
  {
    //i1 + n1 * ( i2 + n2 * ( i3 + n3 * ( ... nx-1 * (ix) ) ) )
    Temp = (tLoc->Dim - IS_ZERO_BASED_ARRAYS) + (tTot->Dim) * get_product(tLoc->Next, tTot->Next); 
    if(Temp < 0) return -1;
    return Temp;
  }
  else
  {
    return (tLoc->Dim - IS_ZERO_BASED_ARRAYS);
  }

}

template<class aType>
int SSI_CPtrArray<aType>::to_index(DimNode Loc)
{
  DimNode Curr = Loc;
  DimNode Tot = Top;

  int Ret = 0;

  Ret = get_product(Curr, Tot);

  if(Ret < 0) return -1;
  return Ret;
}

template<class aType>
int SSI_CPtrArray<aType>::update_array_size(DimNode NewDims)
{
  DimNode Curr = Top;
  DimNode New = NewDims;
  int DimCtr = 1;  // Counts the Dimension to Replace
  
  while(New)
  {
    if(Curr)
    {
      if(New->Dim > Curr->Dim)
      {
        set_dimension(DimCtr, New->Dim);
      }
      Curr = Curr->Next;
    }
    else
    {
      set_dimension(DimCtr, New->Dim);
    }
    New = New->Next;
    DimCtr++;
  }
  return 1;
}

template<class aType>
int SSI_CPtrArray<aType>::check_dim(DimNode Loc)
{
  DimNode Curr = Top;
  DimNode New = Loc;
  int Dim = 1;
  
  while(New)
  {
    if(Curr)
    {
      if(New->Dim > Curr->Dim)
      {
        return 0;
      }
      Curr = Curr->Next;
    }
    else
    {
      return 0;
    }
    New = New->Next;
  }
  return 1;
}



#endif // !defined(AFX_PTRARRAY_H__3F1FB505_C6B5_4BC4_A4CF_B267F79112F6__INCLUDED_)



